<?php

namespace pmvc\ioc\xml;

use pmvc\util\StringUtil;

use pmvc\ioc\ObjectFactory;
use pmvc\ioc\DescriptorObjectFactory;

use Exception;
use DOMDocument;
use DOMXPath;
use ReflectionClass;

/**
 * An {@link ObjectFactory} that uses XML
 * configuration files for configuration.
 *
 */
class XmlObjectFactory
	extends DescriptorObjectFactory {

	const EXTENSION_REGISTRAR_URI_PREFIX = "extension:/";

	/**
	 * Creates the {@link XmlObjectFactory} using the
	 * given xml files.
	 * @param $xmlFiles the files to add
	 */
	public function __construct($xmlFiles=Array(), ObjectFactory $parent=NULL) {
		parent::__construct($parent);
		foreach ($xmlFiles as $xmlFile) {
			$this->addXmlFile($xmlFile);
		}
	}

	/**
	 * Adds an xml object configuration file to the
	 * {@link ObjectFactory}.
	 * @param string $xmlFile the file to add
	 */
	public function addXmlFile($xmlFile) {

		// create andload the document
		$doc = new DOMDocument("1.0");
		if (!$doc->load($xmlFile)) {
			throw new Exception(
				"Unable to parse context file: ".$xmlFile);
		}

		// create the parse context
		$pc 	= new ParseContext($doc, new DOMXPath($doc), $xmlFile, $this);
		$xpath	= $pc->getXPath();

		// register default extensions
		$pc->registerExtension("", "import", 		'pmvc\ioc\xml\ImportExtension');
		$pc->registerExtension("", "properties", 	'pmvc\ioc\xml\PropertiesExtension');
		$pc->registerExtension("", "autowire", 		'pmvc\ioc\xml\AutowireExtension');
		$pc->registerExtension("", "object", 		'pmvc\ioc\xml\ObjectExtension');

		// load extensions
		foreach ($xpath->query("namespace::*", $doc->documentElement) as $node) {

			// get ns info
			list($ns, $name) = explode(":", $node->nodeName, 2);
			$uri = $node->namespaceURI;

			// bail if it's not what we're looking for
			if ($ns!="xmlns") { continue; }
			if (!StringUtil::startsWith($uri, self::EXTENSION_REGISTRAR_URI_PREFIX)) {
				continue;
			}

			// parse the registrar class
			$registrarClass = substr($uri, strlen(self::EXTENSION_REGISTRAR_URI_PREFIX));
			$registrarClass	= str_replace("/", "\\", $registrarClass);

			// create registrar
			$clazz = new ReflectionClass($registrarClass);
			$registrar = $clazz->newInstance();

			// register extensions
			foreach ($registrar->getExtensions() as $elementName => $extensionClass) {
				$pc->registerExtension($name, $elementName, $extensionClass);
			}
		}

		// load properties
		foreach ($doc->documentElement->childNodes as $node) {
			if ($node->nodeType!=XML_ELEMENT_NODE) {
				continue;
			}

			// get namespace and tag name
			$idx 	= strpos($node->nodeName, ":");
			$ns 	= $idx!==false ? substr($node->nodeName, 0, $idx) : "";
			$name	=  $node->localName;

			// find extension
			$ext = $pc->getExtension($ns, $name);
	
			// process it
			$ext->process($node, $pc);
		}
	}

}

?>